package main

import (
	project "2024-fall-etohlcin3-backend/cmd/kitex_gen/industics/blockchain/project/projectservice"
	"crypto/x509"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"strconv"
	"time"

	"github.com/cloudwego/kitex/pkg/generic"
	"github.com/cloudwego/kitex/pkg/rpcinfo"
	"github.com/cloudwego/kitex/server"
	"github.com/hyperledger/fabric-gateway/pkg/client"
	"github.com/hyperledger/fabric-gateway/pkg/hash"
	"github.com/hyperledger/fabric-gateway/pkg/identity"
	etcd "github.com/kitex-contrib/registry-etcd"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
)

func main() {
	orgNumStr := strconv.Itoa(5)
	mspID := "Org" + orgNumStr + "MSP"
	cryptoPath := "./org" + orgNumStr + ".example.com"
	certPath := cryptoPath + "/users/User1@org" + orgNumStr + ".example.com/msp/signcerts"
	keyPath := cryptoPath + "/users/User1@org" + orgNumStr + ".example.com/msp/keystore"
	tlsCertPath := cryptoPath + "/peers/peer0.org" + orgNumStr + ".example.com/tls/ca.crt"
	peerEndpoint := "dns:///blc-service:3205" + orgNumStr
	gatewayPeer := "peer0.org" + orgNumStr + ".example.com"
	clientConnection := newGrpcConnection(tlsCertPath, peerEndpoint, gatewayPeer)
	defer clientConnection.Close()
	id := newIdentity(certPath, mspID)
	sign := newSign(keyPath)

	gw, err := client.Connect(
		id,
		client.WithSign(sign),
		client.WithHash(hash.SHA256),
		client.WithClientConnection(clientConnection),
		// Default timeouts for different gRPC calls
		client.WithEvaluateTimeout(50*time.Second),
		client.WithEndorseTimeout(150*time.Second),
		client.WithSubmitTimeout(50*time.Second),
		client.WithCommitStatusTimeout(10*time.Minute),
	)
	if err != nil {
		panic(err)
	}
	defer gw.Close()

	chaincodeName := "cc" + orgNumStr
	channelName := "cn" + orgNumStr

	network := gw.GetNetwork(channelName)
	ProjectOrg = network.GetContract(chaincodeName)

	r, err := etcd.NewEtcdRegistry([]string{"etcd.default.svc.cluster.local:2379"})
	// r, err := etcd.NewEtcdRegistry([]string{"localhost:2379"})
	if err != nil {
		log.Fatal(err)
	}

	p, err := generic.NewThriftFileProvider("project.thrift")
	if err != nil {
		panic(err)
	}

	g, err := generic.JSONThriftGeneric(p)
	if err != nil {
		panic(err)
	}

	addr, _ := net.ResolveTCPAddr("tcp", ":9090")

	svr := project.NewServer(new(ProjectServiceImpl),
		server.WithGeneric(g),
		server.WithServiceAddr(addr),
		server.WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{
			ServiceName: "project",
		}),
		server.WithRegistry(r))

	err = svr.Run()

	if err != nil {
		log.Println(err.Error())
	}
}

// newGrpcConnection creates a gRPC connection to the Gateway server.
func newGrpcConnection(tlsCertPath string, peerEndpoint string, gatewayPeer string) *grpc.ClientConn {
	certificatePEM, err := os.ReadFile(tlsCertPath)
	if err != nil {
		panic(fmt.Errorf("failed to read TLS certifcate file: %w", err))
	}

	certificate, err := identity.CertificateFromPEM(certificatePEM)
	if err != nil {
		panic(err)
	}

	certPool := x509.NewCertPool()
	certPool.AddCert(certificate)
	transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer)

	connection, err := grpc.NewClient(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
	if err != nil {
		panic(fmt.Errorf("failed to create gRPC connection: %w", err))
	}

	return connection
}

// newIdentity creates a client identity for this Gateway connection using an X.509 certificate.
func newIdentity(certPath string, mspID string) *identity.X509Identity {
	certificatePEM, err := readFirstFile(certPath)
	if err != nil {
		panic(fmt.Errorf("failed to read certificate file: %w", err))
	}

	certificate, err := identity.CertificateFromPEM(certificatePEM)
	if err != nil {
		panic(err)
	}

	id, err := identity.NewX509Identity(mspID, certificate)
	if err != nil {
		panic(err)
	}

	return id
}

// newSign creates a function that generates a digital signature from a message digest using a private key.
func newSign(keyPath string) identity.Sign {
	privateKeyPEM, err := readFirstFile(keyPath)
	if err != nil {
		panic(fmt.Errorf("failed to read private key file: %w", err))
	}

	privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)
	if err != nil {
		panic(err)
	}

	sign, err := identity.NewPrivateKeySign(privateKey)
	if err != nil {
		panic(err)
	}

	return sign
}

func readFirstFile(dirPath string) ([]byte, error) {
	dir, err := os.Open(dirPath)
	if err != nil {
		return nil, err
	}

	fileNames, err := dir.Readdirnames(1)
	if err != nil {
		return nil, err
	}

	return os.ReadFile(path.Join(dirPath, fileNames[0]))
}
